AWT stands for Abstract Window ToolKit. It is a portable GUI library between Solaris and Windows 95/NT and Mac System 7.X(soon) for stand-alone applications and/or applets. Since it can be used in applets it can be used on IRIX, SunOS, HP/UX, Linux which Netscape 2.0 supports.
The Abstract Window Toolkit provides many classes for programmers to use. It is your connection between your application and the native GUI. The AWT hides you from the underlying details of the GUI your application will be running on and thus is at very high level of abstraction. It takes the lowest common denominator approach to retain portability. No floating toolbars or Balloon help here...
It is a Java package and can be used in any Java program by importing java.awt.* via the import keyword. The documentation for the package is available at the Java hompage. The package will be covered briefly as this document is not considered advanced material because it does not discuss Peers, ImageConsumers/Producers, Toolkits and other advanced AWT ilk. It is recommend you look at the source code to see how the AWT really works.
Java concepts
But first lets cover some basics that you'll need to know before diving into the AWT package. I briefly mention some fundamental Java concepts and if they ring a bell then you should continue, otherwise, you should get a better grasp of Java first.
Classes: public, protected and private
Almost every thing is a class in Java, except for primitives such as ints, chars, doubles, etc. And all classes(reference types according to the lang spec) are derived from Object.
The cornerstone of most OO programing languages are classes. So I'd expect you to understand them before using the AWT.
Java has four types of access levels and one unamed default one:
ΓÇópublic: Accessible everywhere. ΓÇóprivate: Accessible only within the class ΓÇóprotected: Accessible to those within the same file(package) and/or derived classes. ΓÇóprivate protected: Acessible to those within the class and derived classes. This is analogous to C++'s protected member access. Introduced in JDK beta 2.
The default access level is very similar to protected.
this and super
this refers to the current instance of class, where as super refers to the parent of the class.
public class MyClass extends Object
{
int x;
int y;
int z;
...
public void myMethod(int x, int y, int size)
{
this.x = x;
this.y = y
z = size;
// Error: Ambiguous
x = x;
y = y
}
}
public class YourClass extends MyClass
{
int x;
int y;
int size;
...
public void yourMethod(int x, int y)
{
// This sets MyClass' x, y,z variables
super.x = x;
super.y = y;
super.z = x + y;
this.x = x;
this.y = y;
size = x * y * super.x * super.y;
}
}
Class methods and variables vs. Instance methods and variables
A static member belongs to the class and not to the instance, thus it is illegal to access a static member from a non-static method without qualifying it i.e
public class AnotherClass
{
static int x;
int y;
public void myMethod()
{
y = AnotherClass.x;
}
}
If you have a C++ background this is rather straightforward. An instance method/variable can be access using the "this" pointer or not. In the above code y and this.y are exactly the same.
With class methods and variables you do not need to create an instance of the class in order to use the method/variables. For example, System.out.println() is accessible without creating an instance of System because out is a static PrintStream.
Inheritance and Interfaces:
With class inheritance you inherit the interface and implementation. Whereas with interfaces you just inherit the interface. In Java, only single inheritance with classes is possible, but interfaces can be multiply inherited.
interface Document
{
public void read();
public void write();
}
public class MultipleInterfaces extends Object implements Runnable, Document
{
// Runnable requires this
public void run() {}
// Document require these
public void read() {}
public void write() {}
}
When you inherit from a class
You inherit the public members, have access to protected members of the parent, but not to private member of the parent. The new 'derived'(child by others) is considered a sub-class or sub-type of the class you derived from.
When you implement an interface
You must implement the methods specified by the interface otherwise the class becomes abstract. See above.
Polymorphism and Object
Since every class is an Object(it's the ultimate parent of all classes) we can do this..
public class ThisClass
{
public myMethod(Object x, Object y)
{
if(x instanceof Button && y instanceof Button)
System.out.println("This method was called with Buttons");
else if(x instanceof Thread && y instanceof Thread)
System.out.println("This method was callled with Threads");
else if(x instanceof Label && y instanceof String)
{
Label l = (Label)x;
l.setLabel((String)y);
}
}
}
Comfortable with what has been discussed? If yes, you should read this and the tutorial. If not you could read this for a brief overview of AWT programming. <Picture>
Structure of the AWT
The structure of the AWT is rather simple: Components are added to and then laid out by layoutmanagers in Containers. We have a variety of event handling, menu, fonts, graphics classes in addition to those two, but they'll be only briefly discussed here.
Nothing prevents us from using threads, audio, I/O, networking classes alongside the AWT. The AWT by itself is rather bare and empty. Imagine a text editor that couldn't save! As a side note, the AWT can be used in a stand-alone Java application or in an applet that is embedded within a HTML document.
There are two user-interface classes in the AWT to focus on: Components and Containers.
Containers
Containers(Frames, Dialogs, Windows and Panels) can contain components and are themselves components, thus can be added to Containers. Containers usually handle events that occurred to the Components, although nothing prevents you from handling events in the component. In short all classes derived from Container can be one.
The method of handling events in the Container(i.e Frame) is preferred over the latter, since we want to centralize event handling. If you don't want to handle events in one common area of code, then you must sub-class every Component you create an instance of and override its action() or handleEvent() method.
public class TextEditor extends Frame
{
Button myButton;
public TextEditor()
{
...
setTitle("Text Editor")
setLayout(new BorderLayout());
setBackground(Color.orange);
myButton = new Button("Hit This");
add("Center", myButton);
pack();
show();
}
public boolean handleEvent(Event evt)
{
if("Hit This".equals(evt.arg))
{
System.out.println("He hit me");
return true;
}
}
}
This is another method of event handling. The event is handled in the Component rather than the Container. First we must sub-class Button.
public class MyButton extends Button
{
...
...
public boolean action(Event evt, Object arg)
{
if(evt.target == this)
{
System.out.println("He hit me");
return true;
}
}
}
public class AnotherTextEditor extends Frame
{
MyButton anotherButton;
public AnotherTextEditor()
{
setTitle("Another Text Editor")
setLayout(new BorderLayout());
setBackground(Color.white);
anotherButton = new MyButton("Hit This");
add("Center", anotherButton);
pack();
resize(300, 300);
show();
}
}
All Containers have common functionality, due to the fact they are derived from Container which includes many pre-defined event handling methods(called callbacks). These events are useful for handling user input in a specialized(i.e sub-class or derived) Container class where you'd override the default behavior such as the appearance(i.e font, color, etc.) by overriding the methods.
A quick-and-dirty summary of common methods:
add(Component)
add(String, Component)
remove(Component)
getComponents()
setLayout(LayoutManager)
getLayout()
Every Container Is-A Component since it is derived from Component, thus it can also behave and be used like an Component. All of the methods in Component can be used in a Container.
Components
Components are generally the stuff that the user interacts with. The meat and potatoes of your application. You'll need to display Windows and Buttons. You'll want Lists within Windows. You'll want the user to enter some text/input. You'll want easy access to features and functions. Components will do that for you.
Components are Buttons, TextAreas, Scrollbars, etc. in other words the visible UI controls that the user interacts with, all of which have been added to a Container. Anything that is derived from the class Component can be one. p
How Components are "laid out" within a Container is described by the LayoutManager class. Since the LayoutManager class is abstract, we can not use it directly. You must sub-class it and provide your own functionality or use a derived class of LayoutManager(i.e BorderLayout, CardLayout, GridLayout, etc) already created for you.
There are man different layout schemes, but the ones pre-defined for us are
ΓÇóBorderLayout This scheme lays out the Component in 5 ways
1.North - Northern part of the Container 2.South - Southern part of the Container 3.East - Eastern part of the Container 4.West - Western part of the Container 5.Center - Centered in the Container
ΓÇóCardLayout - Allows for what Windows programmers have called for years "tabbed dialogs" or dynamic dialogs(now available on all versions of Netscape). ΓÇóGridLayout - Allows for the layout of Components in a grid -like fashion rather than "North" or "Center". ΓÇóGridBagLayout - HTMLTable-ish style of layout ΓÇóFlowLayout - Allows for Component to be laid out in a row(or flow) and aligned(left, right, center). ΓÇóNone - No layout, the Container will not attempt to reposition the Components during a update.
To use a layout we must call setLayout() for the Container with an instance of a LayoutManager(since LayoutManager is abstract, a sub-class will do).
Using each layouts is different. For BorderLayout you specify the type of layout(North, South, etc.) by passing a string(i.e "North", "South", etc to the method add() in a Container.
i.e
add("Center", myComponent);
adds myComponent to the Container and centers it. With other layouts, the first String parameter is different.
Using Components and Containers
Like all Java classes, to use a class from the AWT we need to create an instance of it via "new".
add(new Button("Hello"));
Button b = new Button("World");
add(b);
ΓÇóThe first add creates a new instance of a Button before adding it to the Container. To a C++ programmer, this is bizarre. "new Button" never gets assigned to a variable, so you can't delete it. When do you delete the Button? Never. Java has automatic garbage collection and will free the memory up when the Button is no longer used(i.e b = null and calling gc() may remove it).
You can handle events given the label of the Component, thus new Button("Hello") makes perfect sense to a Java programmer. ΓÇóThe second add(), adds the previously created component 'b'(a Button) to the Container.
Event handling
Event-handling? Geez don't I just poll and grab user-input?
No, this is Java and how the GUIs it runs under operate. GUIs and Windowing systems use the event-driven model rather than the old procedural-grab-the-input-yourself model. The OS is the one polling for events. When it finds ones, it tells you what has happened. If you say, "bah, I don't care about that event". Fine, the OS handles it with its own default handler, otherwise you better do something with it or you get the default, which maybe nothing at all.
Here are the some of the events you will need to handle. This list of all events your application will probably need to handle is in the Event's class documentation.
ACTION_EVENT occurs when you press a button, select a menu, etc.
To handle ACTION_EVENT you have two choices
1.Handle it handleEvent() 2.Handle it in action()
Both are located in the Component class so all derived classes have these methods and you should override them to provide your own custom behavior.
The following examples shows you how and why...
public boolean handleEvent(Event evt)
{
switch(evt.id)
{
case Event.ACTION_EVENT:
{
if((Hey I've been hit " + n + "times).equals(evt.arg))
{
return true;
}
// Here evt.target contains the target
// i.e java.awt.MenuItem[label=Quit]
// evt.arg contains the label of the button pressed
// or menu item hit
}
default:
return false;
}
}
The same thing came be accomplished with action(evt, arg), because when an ACTION_EVENT occurs action() is called.
public boolean action(Event evt, Object arg)
{
if(("Hey I've been hit " + n + "times").equals(arg))
{
return true;
}
return false;
}
As you can see, even if the labels change you can still receive the event. The above is a button that changes everytime you hit it. But this isn't the only way to handle events. In fact you can compare objects instead of labels such as if(evt.target == myButton).
public boolean handleEvent(Event evt)
{
switch(evt.id)
{
// Examples of what can be handled
case Event.KEY_PRESS:
case Event.HOME:
case Event.WINDOW_MOVED:
return true;
// ...
default:
return false;
}
}
Also you can override a sub-class's method handler such as keyDown(), mouseDown() to provide your own event handling code. For example a specialized(sub-class) version of a TextArea that overrides mouseEnter() can determine when the mouse enters the Component and thus can decide to hilight the text if needed.
The infamous and canonical example will be shown here to demostrate simple features of the AWT. This example does not use the AWT at all and displays the string "Hello World" to the standard output. You ask "Why doesn't it display to a Window"?
import java.*;
public class MyHelloWorld
{
public static void main(String args[])
{
System.out.println("Hello World");
}
}
Have we created a Window? have we specified the font size, color and placement of the string "Hello World"? No. GUIs were never meant for printing "Hello World" onto the screen graphically, but to make applications easier-to-use.
This example displays the string graphically and shows you the types of errors you can get into if you are not careful.
Menus are at the center of your application. The difference between a usuable application and one that is absolutely frustrating lies in the organization of your menus. The user-interfaces rules for a good menu design are not clear.
The following classes are related to Menus
ΓÇóMenuComponent
ΓÇóMenuBar ΓÇóMenuItem
ΓÇóCheckboxMenuItem ΓÇóMenu
Handling is done in the handleEvent() method which you will override.
To use menus you need to create a MenuBar and Menus with MenuItems, then attach the MenuBar to your application via setMenuBar. You can even embed, Menus in MenuItems..
MenuBar mb = new MenuBar();
Menu top = new Menu("File");
Menu sub = new Menu("New");
sub.add(new MenuItem("Document"));
sub.add(new MenuItem("Message"));
sub.add(new MenuItem("Image"));
top.add(sub);
top.add(new MenuItem("Open"));
top.add(new MenuItem("Save..));
setMenuBar(mb);
To use Menus in applet you'll need to create a new Frame and attach a menubar to that. You can not just change the browser's menubar. You are not dealing with OLE 2 here.
Simple Graphics
Drawing Lines:
If you want to draw a line this is the method you would use
g.drawLine(x1, y1, width, height);
Where g is an instance of the Graphics class. graphics.drawLine(...) is also legal as long as 'graphics' is an instance of Graphics.
Note: Graphics is an abstract class, you cannot just 'new' it.
i.e
g = new Graphics() is illegal.
Here is some code that demostrates drawLine. The example was converted from C/C++ from a book I can't remember the title of.